<?php
/**
 * 控制器基类
 *
 * @author Claude Code
 * @since 2025-11-03
 */

namespace app;

use think\App;
use think\Request;
use think\Response;
use think\Validate;
use think\facade\Log;
use think\facade\Cache;

/**
 * 控制器基类
 *
 * 优化特性：
 * 1. 统一的参数处理
 * 2. 标准化的响应格式
 * 3. 自动验证机制
 * 4. 全局中间件支持
 * 5. 完善的日志记录
 * 6. 跨域处理
 * 7. 权限检查
 * 8. 分页参数处理
 */
abstract class BaseController
{
    /**
     * Request实例
     * @var Request
     */
    protected $request;

    /**
     * 应用实例
     * @var App
     */
    protected $app;

    /**
     * 当前用户
     * @var array|null
     */
    protected $user;

    /**
     * 分页参数
     * @var array
     */
    protected $pageParams = [
        'page' => 1,
        'limit' => 15
    ];

    /**
     * 默认页码
     * @var int
     */
    protected $defaultPage = 1;

    /**
     * 默认每页数量
     * @var int
     */
    protected $defaultLimit = 15;

    /**
     * 最大每页数量
     * @var int
     */
    protected $maxLimit = 100;

    /**
     * 控制器中间件
     * @var array
     */
    protected $middleware = [
        // 可以在子类中覆盖或追加
    ];

    /**
     * 构造方法
     * @access public
     */
    public function __construct(App $app)
    {
        $this->app = $app;
        $this->request = $this->app->request;

        // 控制器初始化
        $this->initialize();

        // 注册控制器中间件
        $this->registerMiddleware();
    }

    /**
     * 初始化
     * @access protected
     */
    protected function initialize()
    {
        // 优先从请求对象中获取用户信息（由JwtAuth中间件设置）
        if (isset($this->request->userInfo)) {
            $this->user = $this->request->userInfo;
        } else {
            // 如果请求对象中没有，则使用原有的获取方式
            $this->user = $this->getCurrentUser();
        }

        // 处理分页参数
        $this->handlePageParams();

        // 记录请求日志
        $this->logRequest();

        // 全局参数处理
        $this->handleGlobalParams();
    }
    
    /**
     * 记录调试日志
     * @access protected
     * @param string $message 消息
     * @param array $context 上下文
     * @return void
     */
    protected function logDebug(string $message, array $context = []): void
    {
        // 只在开发环境记录调试日志
        if (env('APP_ENV') === 'dev') {
            $this->log('debug', $message, $context);
        }
    }

    /**
     * 注册中间件
     * @access protected
     */
    protected function registerMiddleware()
    {
        // 子类可以重写此方法来注册中间件
    }

    /**
     * 获取当前用户
     * @access protected
     * @return array|null
     */
    protected function getCurrentUser(): ?array
    {
        // 首先尝试从Request对象的userInfo属性获取（由JwtAuth中间件设置）
        if (isset($this->request->userInfo)) {
            return $this->request->userInfo;
        }

        // 如果没有，则尝试从Token中获取用户信息（兼容性处理）
        $token = $this->getTokenFromRequest();
        if (!$token) {
            return null;
        }

        // 解析JWT Token
        return $this->parseJwtToken($token);
    }

    /**
     * 从请求中获取Token
     * @access protected
     * @return string|null
     */
    protected function getTokenFromRequest(): ?string
    {
        // 优先级：Header > Param > Cookie
        $token = $this->request->header('Authorization');
        if (!$token) {
            $token = $this->request->param('token');
        }
        if (!$token) {
            $token = $this->request->cookie('token');
        }

        // 清理Bearer前缀
        if ($token && strpos($token, 'Bearer ') === 0) {
            $token = substr($token, 7);
        }

        return $token ?: null;
    }

    /**
     * 解析JWT Token
     * @access protected
     * @param string $token JWT Token
     * @return array|null
     */
    protected function parseJwtToken(string $token): ?array
    {
        try {
            // 使用Firebase JWT库解析token
            $jwtKey = \think\facade\Config::get('jwt.key', 'GOUGUOA');
            \think\facade\Log::info('JWT解析开始', ['jwtKey' => $jwtKey, 'token_length' => strlen($token)]);
            $decoded = \Firebase\JWT\JWT::decode($token, new \Firebase\JWT\Key($jwtKey, 'HS256'));
            \think\facade\Log::info('JWT解析成功', ['decoded_sub' => $decoded->sub ?? 'null']);

            // 从JWT payload中获取用户ID
            $userId = $decoded->sub ?? 0;
            if (!$userId) {
                \think\facade\Log::error('JWT token中没有用户ID', ['token' => $token]);
                return null;
            }

            \think\facade\Log::info('尝试查询用户信息', ['userId' => $userId]);

            // 从数据库获取用户信息
            $user = \think\facade\Db::name('admin')
                ->where('id', $userId)
                ->where('status', 1) // 确保用户状态正常
                ->find();

            if (!$user) {
                \think\facade\Log::error('用户不存在或已禁用', ['userId' => $userId]);
                return null;
            }

            // 获取用户角色信息
            $group = \think\facade\Db::name('admin_group')
                ->where('id', $user['group_id'])
                ->find();

            // 获取部门信息
            $department = null;
            if ($user['did']) {
                $department = \think\facade\Db::name('department')
                    ->where('id', $user['did'])
                    ->find();
            }

            // 获取职位信息
            $position = null;
            if ($user['position_id']) {
                $position = \think\facade\Db::name('position')
                    ->where('id', $user['position_id'])
                    ->find();
            }

            // 组织用户信息
            $userInfo = [
                'id' => $user['id'],
                'username' => $user['username'],
                'nickname' => $user['nickname'],
                'email' => $user['email'],
                'mobile' => $user['mobile'],
                'avatar' => $user['thumb'],
                'gid' => $user['group_id'],
                'group' => $group,
                'did' => $user['did'],
                'department' => $department,
                'pid' => $user['position_id'],
                'position' => $position,
                'status' => $user['status'],
                'login_time' => $user['last_login_time'],
                'login_ip' => $user['last_login_ip'],
                'rules' => $group['rules'] ?? '',
            ];

            return $userInfo;
        } catch (\Exception $e) {
            // 解析失败，记录错误日志
            \think\facade\Log::error('JWT解析失败', [
                'error_message' => $e->getMessage(),
                'error_trace' => $e->getTraceAsString()
            ]);
            return null;
        }
    }

    /**
     * 处理分页参数
     * @access protected
     * @return void
     */
    protected function handlePageParams()
    {
        $page = $this->request->param('page/d', $this->defaultPage);
        $limit = $this->request->param('limit/d', $this->defaultLimit);

        // 限制范围
        $page = max(1, intval($page));
        $limit = max(1, min(intval($limit), $this->maxLimit));

        $this->pageParams = [
            'page' => $page,
            'limit' => $limit
        ];
    }

    /**
     * 获取分页参数
     * @access protected
     * @return array
     */
    protected function getPageParams(): array
    {
        return $this->pageParams;
    }

    /**
     * 记录请求日志
     * @access protected
     * @return void
     */
    protected function logRequest()
    {
        $logData = [
            'ip' => $this->request->ip(),
            'method' => $this->request->method(),
            'path' => $this->request->pathinfo(),
            'param' => $this->request->param(),
            'user_agent' => $this->request->header('user-agent'),
            'timestamp' => time()
        ];

        if ($this->user) {
            $logData['user_id'] = $this->user['id'] ?? null;
        }

        // 只记录需要审计的接口
        $auditPaths = ['admin/user/add', 'admin/user/update', 'admin/user/delete'];
        if (in_array($this->request->pathinfo(), $auditPaths)) {
            Log::channel('audit')->info('User Operation', $logData);
        }

        // 记录错误日志
        $this->request->has('__log_error__') && Log::channel('error')->warning('Request Error', $logData);
    }

    /**
     * 全局参数处理
     * @access protected
     * @return void
     */
    protected function handleGlobalParams()
    {
        // 可以在这里处理全局参数，如：
        // - 语言设置
        // - 时区设置
        // - 请求频率限制
        // - IP白名单检查
    }

    /**
     * 检查权限
     * @access protected
     * @param string $action 操作
     * @return bool
     */
    protected function checkPermission(string $action): bool
    {
        // 子类应实现具体的权限检查逻辑
        // 这里返回true表示允许访问
        return true;
    }

    /**
     * 检查是否需要登录
     * @access protected
     * @return bool
     */
    protected function needLogin(): bool
    {
        // 白名单路径不需要登录
        $whiteList = [
            'admin/user/login',
            'admin/user/captcha',
            'admin/user/logout'
        ];

        $path = $this->request->pathinfo();
        return !in_array($path, $whiteList);
    }

    /**
     * 成功响应
     * @access protected
     * @param mixed $data 返回数据
     * @param string $message 提示信息
     * @param int $code 状态码
     * @param array $extra 额外信息
     * @return Response
     */
    protected function success($data = null, string $message = '操作成功', int $code = 0, array $extra = []): Response
    {
        $response = [
            'code' => $code,
            'msg' => $message,
            'time' => time(),
            'data' => $data
        ];

        if (!empty($extra)) {
            $response = array_merge($response, $extra);
        }

        return json($response);
    }

    /**
     * 错误响应
     * @access protected
     * @param string $message 错误信息
     * @param int $code 错误码
     * @param int $httpCode HTTP状态码
     * @param mixed $data 返回数据
     * @return Response
     */
    protected function error(string $message = '操作失败', int $code = 1, int $httpCode = 400, $data = null): Response
    {
        $response = [
            'code' => $code,
            'msg' => $message,
            'time' => time(),
            'data' => $data
        ];

        return json($response, $httpCode);
    }

    /**
     * 分页响应
     * @access protected
     * @param mixed $list 数据列表
     * @param int $total 总数
     * @param int|null $page 页码
     * @param int|null $limit 每页数量
     * @return Response
     */
    protected function paginate($list, int $total, ?int $page = null, ?int $limit = null): Response
    {
        $page = $page ?? $this->pageParams['page'];
        $limit = $limit ?? $this->pageParams['limit'];

        return $this->success([
            'list' => $list,
            'total' => $total,
            'page' => $page,
            'limit' => $limit,
            'pages' => ceil($total / $limit)
        ]);
    }

    /**
     * 未授权响应
     * @access protected
     * @param string $message 错误信息
     * @return Response
     */
    protected function unauthorized(string $message = '未登录或登录已过期'): Response
    {
        return $this->error($message, 401, 401);
    }

    /**
     * 禁止访问响应
     * @access protected
     * @param string $message 错误信息
     * @return Response
     */
    protected function forbidden(string $message = '没有权限访问'): Response
    {
        return $this->error($message, 403, 403);
    }

    /**
     * 资源不存在响应
     * @access protected
     * @param string $message 错误信息
     * @return Response
     */
    protected function notFound(string $message = '资源不存在'): Response
    {
        return $this->error($message, 404, 404);
    }

    /**
     * 服务器错误响应
     * @access protected
     * @param string $message 错误信息
     * @param \Exception|null $e 异常
     * @return Response
     */
    protected function serverError(string $message = '服务器内部错误', ?\Exception $e = null): Response
    {
        // 记录错误日志
        Log::channel('error')->error('Server Error', [
            'message' => $message,
            'file' => $e ? $e->getFile() : '',
            'line' => $e ? $e->getLine() : '',
            'trace' => $e ? $e->getTraceAsString() : '',
            'request' => $this->request->param()
        ]);

        return $this->error($message, 500, 500);
    }

    /**
     * 验证数据
     * @access protected
     * @param array $data 数据
     * @param Validate|array|string $validate 验证器
     * @param array $message 提示信息
     * @param bool $batch 是否批量验证
     * @return bool
     */
    protected function validate(array $data, $validate, array $message = [], bool $batch = true): bool
    {
        if (is_array($validate)) {
            $v = new Validate();
            $v->message($message)->batch($batch);
            if (!$v->check($data, $validate)) {
                $this->error($v->getError(), 422, 422);
                return false;
            }
            return true;
        }

        if (is_string($validate)) {
            $v = new $validate();
            if (!$v->message($message)->batch($batch)->check($data)) {
                $this->error($v->getError(), 422, 422);
                return false;
            }
            return true;
        }

        return false;
    }

    /**
     * 获取请求参数
     * @access protected
     * @param string|null $name 参数名
     * @param mixed $default 默认值
     * @param string|null $filter 过滤方法
     * @return mixed
     */
    protected function requestParam(string $name = null, $default = null, ?string $filter = null)
    {
        if ($name === null) {
            return $this->request->param();
        }

        $value = $this->request->param($name, $default, $filter);

        return $value;
    }

    /**
     * 获取请求头
     * @access protected
     * @param string|null $name 头名
     * @param mixed $default 默认值
     * @return mixed
     */
    protected function requestHeader(string $name = null, $default = null)
    {
        if ($name === null) {
            return $this->request->header();
        }

        return $this->request->header($name, $default);
    }

    /**
     * 检查是否为POST请求
     * @access protected
     * @return bool
     */
    protected function isPost(): bool
    {
        return $this->request->isPost();
    }

    /**
     * 检查是否为GET请求
     * @access protected
     * @return bool
     */
    protected function isGet(): bool
    {
        return $this->request->isGet();
    }

    /**
     * 检查是否为AJAX请求
     * @access protected
     * @return bool
     */
    protected function isAjax(): bool
    {
        return $this->request->isAjax();
    }

    /**
     * 获取客户端IP
     * @access protected
     * @return string
     */
    protected function getClientIp(): string
    {
        return $this->request->ip();
    }

    /**
     * 获取当前路由
     * @access protected
     * @return array
     */
    protected function getCurrentRoute(): array
    {
        return [
            'controller' => $this->request->controller(),
            'action' => $this->request->action(),
            'path' => $this->request->pathinfo()
        ];
    }

    /**
     * 设置缓存
     * @access protected
     * @param string $key 缓存键
     * @param mixed $value 缓存值
     * @param int|null $expire 过期时间
     * @return bool
     */
    protected function setCache(string $key, $value, ?int $expire = null): bool
    {
        return Cache::set($key, $value, $expire);
    }

    /**
     * 获取缓存
     * @access protected
     * @param string $key 缓存键
     * @param mixed $default 默认值
     * @return mixed
     */
    protected function getCache(string $key, $default = null)
    {
        return Cache::get($key, $default);
    }

    /**
     * 删除缓存
     * @access protected
     * @param string $key 缓存键
     * @return bool
     */
    protected function deleteCache(string $key): bool
    {
        return Cache::delete($key);
    }

    /**
     * 清空缓存
     * @access protected
     * @param string|null $tag 标签
     * @return bool
     */
    protected function clearCache(?string $tag = null): bool
    {
        if ($tag) {
            return Cache::tag($tag)->clear();
        }
        return Cache::clear();
    }

    /**
     * 记录日志
     * @access protected
     * @param string $level 日志级别
     * @param string $message 消息
     * @param array $context 上下文
     * @return void
     */
    protected function log(string $level, string $message, array $context = []): void
    {
        Log::channel('business')->log($level, $message, $context);
    }

    /**
     * 记录信息日志
     * @access protected
     * @param string $message 消息
     * @param array $context 上下文
     * @return void
     */
    protected function logInfo(string $message, array $context = []): void
    {
        $this->log('info', $message, $context);
    }

    /**
     * 记录警告日志
     * @access protected
     * @param string $message 消息
     * @param array $context 上下文
     * @return void
     */
    protected function logWarning(string $message, array $context = []): void
    {
        $this->log('warning', $message, $context);
    }

    /**
     * 记录错误日志
     * @access protected
     * @param string $message 消息
     * @param array $context 上下文
     * @return void
     */
    protected function logError(string $message, array $context = []): void
    {
        $this->log('error', $message, $context);
    }

    /**
     * 获取当前用户ID
     * @access protected
     * @return int
     */
    protected function getUserId(): int
    {
        // 直接从用户信息中获取ID
        return $this->user['id'] ?? 0;
    }

    /**
     * 获取当前用户信息
     * @access protected
     * @return array|null
     */
    protected function getUserInfo(): ?array
    {
        return $this->user;
    }

    /**
     * 获取当前用户部门ID
     * @access protected
     * @return int
     */
    protected function getUserDepartmentId(): int
    {
        $userInfo = $this->getUserInfo();
        return $userInfo['did'] ?? 0;
    }

    /**
     * 检查是否为超级管理员
     * @access protected
     * @return bool
     */
    protected function checkSuperAdmin(): bool
    {
        $userId = $this->getUserId();
        return $userId === 1; // 假设ID为1的是超级管理员
    }

    /**
     * 参数错误响应
     * @access protected
     * @param string $message 错误信息
     * @param int $httpCode HTTP状态码
     * @return Response
     */
    protected function paramError(string $message = '参数错误', int $httpCode = 422): Response
    {
        return $this->error($message, 422, $httpCode);
    }

    /**
     * 创建成功响应
     * @access protected
     * @param mixed $data 数据
     * @param string $message 提示信息
     * @return Response
     */
    protected function created($data = null, string $message = '创建成功'): Response
    {
        // 使用标准的成功格式，code=0，HTTP状态码201
        $response = [
            'code' => 0,
            'msg' => $message,
            'time' => time(),
            'data' => $data
        ];

        return json($response, 201);
    }

    /**
     * 删除成功响应
     * @access protected
     * @param string $message 提示信息
     * @return Response
     */
    protected function deleted(string $message = '删除成功'): Response
    {
        // 返回标准JSON格式的成功响应
        $response = [
            'code' => 0,
            'msg' => $message,
            'time' => time(),
            'data' => null
        ];

        return json($response);
    }

    /**
     * 更新成功响应
     * @access protected
     * @param mixed $data 数据
     * @param string $message 提示信息
     * @return Response
     */
    protected function updated($data = null, string $message = '更新成功'): Response
    {
        return $this->success($data, $message);
    }
}
